﻿using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Globalization;
using System.Security.Permissions;
using System.Windows.Forms;

namespace Extra.Windows.Forms
{
	public interface IListViewItemCollection : IList<ListViewItem>
	{
		ListView Owner { get; set; }
	}

	public partial class ListView : Control
	{
		private readonly static Size defaultSize = new Size(0x79, 0x61);

		private ListViewAlignment alignStyle;
		private BorderStyle borderStyle;
		private ColumnHeader[] columnHeaders;
		private bool comctlSupportsVisualStyles = false;
		private bool comctlSupportsVisualStylesTested = false;
		private ColumnHeaderStyle headerStyle;
		private int itemCount;
		private IComparer<ListViewItem> listItemSorter;
		private NativeMethods.ListViewStyle listViewStyle;
		private NativeMethods.ListViewStyleEx listViewStyleEx;
		private bool rightToLeftLayout;
		private SortOrder sorting;
		private List<ListViewItem> tempItems;
		private System.Drawing.Size tileSize;
		private int updateCount;
		private int updateCounter;
		private View viewStyle;
		private ListViewItemCollection newItems;

		public ListView()
		{
			base.SetStyle(ControlStyles.UserPaint, false);
			base.SetStyle(ControlStyles.StandardClick, false);
			base.SetStyle(ControlStyles.UseTextForAccessibility, false);
			base.SetBounds(0, 0, defaultSize.Width, defaultSize.Height);
		}

		[Description("ListViewAfterLabelEditDescr"), Category("Behavior")]
		public event LabelEditEventHandler AfterLabelEdit;
		[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
		public new event EventHandler BackgroundImageLayoutChanged;
		[Description("ListViewBeforeLabelEditDescr"), Category("Behavior")]
		public event LabelEditEventHandler BeforeLabelEdit;
		[Category("Action"), Description("ListViewCacheVirtualItemsEventDescr")]
		public event CacheVirtualItemsEventHandler CacheVirtualItems;
		[Description("ListViewColumnClickDescr"), Category("Action")]
		public event ColumnClickEventHandler ColumnClick;
		[Description("ListViewColumnReorderedDscr"), Category("PropertyChanged")]
		public event ColumnReorderedEventHandler ColumnReordered;
		[Description("ListViewColumnWidthChangedDscr"), Category("PropertyChanged")]
		public event ColumnWidthChangedEventHandler ColumnWidthChanged;
		[Description("ListViewColumnWidthChangingDscr"), Category("PropertyChanged")]
		public event ColumnWidthChangingEventHandler ColumnWidthChanging;
		[Category("Behavior"), Description("ListViewDrawColumnHeaderEventDescr")]
		public event DrawListViewColumnHeaderEventHandler DrawColumnHeader;
		[Category("Behavior"), Description("ListViewDrawItemEventDescr")]
		public event DrawListViewItemEventHandler DrawItem;
		[Description("ListViewDrawSubItemEventDescr"), Category("Behavior")]
		public event DrawListViewSubItemEventHandler DrawSubItem;
		[Category("Action"), Description("ListViewItemClickDescr")]
		public event EventHandler ItemActivate;
		[Category("Behavior"), Description("CheckedListBoxItemCheckDescr")]
		public event ItemCheckEventHandler ItemCheck;
		[Description("ListViewItemCheckedDescr"), Category("Behavior")]
		public event ItemCheckedEventHandler ItemChecked;
		[Description("ListViewItemDragDescr"), Category("Action")]
		public event ItemDragEventHandler ItemDrag;
		[Category("Action"), Description("ListViewItemMouseHoverDescr")]
		public event ListViewItemMouseHoverEventHandler ItemMouseHover;
		[Description("ListViewItemSelectionChangedDescr"), Category("Behavior")]
		public event ListViewItemSelectionChangedEventHandler ItemSelectionChanged;
		[EditorBrowsable(EditorBrowsableState.Never), Browsable(false)]
		public new event EventHandler PaddingChanged;
		[EditorBrowsable(EditorBrowsableState.Never), Browsable(false)]
		public new event PaintEventHandler Paint;
		[Category("Action"), Description("ListViewRetrieveVirtualItemEventDescr")]
		public event RetrieveVirtualItemEventHandler RetrieveVirtualItem;
		[Description("ControlOnRightToLeftLayoutChangedDescr"), Category("PropertyChanged")]
		public event EventHandler RightToLeftLayoutChanged;
		[Description("ListViewSearchForVirtualItemDescr"), Category("Action")]
		public event SearchForVirtualItemEventHandler SearchForVirtualItem;
		[Description("ListViewSelectedIndexChangedDescr"), Category("Behavior")]
		public event EventHandler SelectedIndexChanged;
		[EditorBrowsable(EditorBrowsableState.Never), Browsable(false)]
		public new event EventHandler TextChanged;
		[Description("ListViewVirtualItemsSelectionRangeChangedDescr"), Category("Behavior")]
		public event ListViewVirtualItemsSelectionRangeChangedEventHandler VirtualItemsSelectionRangeChanged;

		[DefaultValue(typeof(ListViewAlignment), "Top")]
		public ListViewAlignment Alignment
		{
			get { return alignStyle; }
			set
			{
				value.CheckValidPropArgEnum();
				if (alignStyle != value)
				{
					alignStyle = value;
					RecreateHandleInternal();
				}
			}
		}

		[DefaultValue(false)]
		public bool AutoArrange
		{
			get { return HasStyle(NativeMethods.ListViewStyle.AutoArrange); }
			set { SetStyle(NativeMethods.ListViewStyle.AutoArrange, value); }
		}

		[DefaultValue(typeof(BorderStyle), "Fixed3D")]
		public BorderStyle BorderStyle
		{
			get { return borderStyle; }
			set
			{
				value.CheckValidPropArgEnum();
				if (borderStyle != value)
				{
					borderStyle = value;
					RecreateHandleInternal();
				}
			}
		}

		public bool GroupsEnabled { get; set; }

		[DefaultValue(typeof(ColumnHeaderStyle), "Clickable")]
		public ColumnHeaderStyle HeaderStyle
		{
			get { return headerStyle; }
			set
			{
				value.CheckValidPropArgEnum();
				if (headerStyle != value)
				{
					headerStyle = value;
					bool hasNoSortHeader = HasStyle(NativeMethods.ListViewStyle.NoSortHeader);
					if ((hasNoSortHeader && (value == ColumnHeaderStyle.Clickable)) || (!hasNoSortHeader && (value == ColumnHeaderStyle.Nonclickable)))
					{
						SetStyle(NativeMethods.ListViewStyle.NoSortHeader, !hasNoSortHeader);
						this.RecreateHandleInternal();
					}
					else
						this.UpdateStyles();
				}
			}
		}

		[DefaultValue(true)]
		public bool HideSelection
		{
			get { return !HasStyle(NativeMethods.ListViewStyle.ShowSelAlways); }
			set { SetStyle(NativeMethods.ListViewStyle.ShowSelAlways, !value); }
		}

		[DefaultValue(false)]
		public bool LabelEdit
		{
			get { return HasStyle(NativeMethods.ListViewStyle.EditLabels); }
			set { SetStyle(NativeMethods.ListViewStyle.EditLabels, value); }
		}

		[DefaultValue(true)]
		public bool LabelWrap
		{
			get { return !HasStyle(NativeMethods.ListViewStyle.NoLabelWrap); }
			set { SetStyle(NativeMethods.ListViewStyle.NoLabelWrap, !value); }
		}

		[DefaultValue(true)]
		public bool MultiSelect
		{
			get { return !HasStyle(NativeMethods.ListViewStyle.SingleSel); }
			set { SetStyle(NativeMethods.ListViewStyle.SingleSel, !value); }
		}

		[DefaultValue(false), Localizable(true)]
		public bool RightToLeftLayout
		{
			get { return rightToLeftLayout; }
			set
			{
				if (rightToLeftLayout != value)
				{
					rightToLeftLayout = value;
					//this.OnRightToLeftLayoutChanged(EventArgs.Empty);
				}
			}
		}

		[DefaultValue(0), Category("Behavior")]
		public SortOrder Sorting
		{
			get { return this.sorting; }
			set
			{
				value.CheckValidPropArgEnum();
				if (sorting != value)
				{
					sorting = value;
					if ((this.View == View.LargeIcon) || (this.View == View.SmallIcon))
					{
						if (this.listItemSorter == null)
							this.listItemSorter = new IconComparer(this.sorting);
						else if (this.listItemSorter is IconComparer)
							((IconComparer)this.listItemSorter).SortOrder = this.sorting;
					}
					else if (value == SortOrder.None)
					{
						this.listItemSorter = null;
					}
					if (value == SortOrder.None)
						base.UpdateStyles();
					else
						this.RecreateHandleInternal();
				}
			}
		}

		[EditorBrowsable(EditorBrowsableState.Never), Browsable(false), Bindable(false)]
		public override string Text
		{
			get { return base.Text; }
			set { base.Text = value; }
		}

		[Category("Appearance"), Browsable(true)]
		public Size TileSize
		{
			get
			{
				if (!this.tileSize.IsEmpty)
				{
					return this.tileSize;
				}
				if (base.IsHandleCreated)
				{
					NativeMethods.LVTILEVIEWINFO lParam = new NativeMethods.LVTILEVIEWINFO(NativeMethods.ListViewTileViewMask.TileSize);
					NativeMethods.SendMessage(base.Handle, NativeMethods.ListViewMessage.GetTileViewInfo, 0, lParam);
					return lParam.TileSize;
				}
				return Size.Empty;
			}
			set
			{
				if (this.tileSize != value)
				{
					if ((value.IsEmpty || (value.Height <= 0)) || (value.Width <= 0))
						throw new ArgumentOutOfRangeException("TileSize", "ListViewTileSizeMustBePositive");
					this.tileSize = value;
					if (base.IsHandleCreated)
					{
						NativeMethods.LVTILEVIEWINFO lParam = new NativeMethods.LVTILEVIEWINFO(0) { TileSize = value };
						NativeMethods.SendMessage(base.Handle, NativeMethods.ListViewMessage.SetTileViewInfo, 0, lParam);
						if (this.AutoArrange)
							this.UpdateListViewItemsLocations();
					}
				}
			}
		}

		[DefaultValue(typeof(View), "LargeIcon")]
		public View View
		{
			get { return viewStyle; }
			set
			{
				//if ((value == View.Tile) && this.CheckBoxes)
				//	throw new NotSupportedException("ListViewTileViewDoesNotSupportCheckBoxes");
				//this.FlipViewToLargeIconAndSmallIcon = false;
				value.CheckValidPropArgEnum();
				//if ((value == View.Tile) && this.VirtualMode)
				//	throw new NotSupportedException(SR.GetString("ListViewCantSetViewToTileViewInVirtualMode"));
				if (viewStyle != value)
				{
					viewStyle = value;
					if (base.IsHandleCreated && this.ComctlSupportsVisualStyles)
					{
						this.SendMessage((uint)NativeMethods.ListViewMessage.SetView, (int)this.viewStyle, 0);
						this.UpdateGroupView();
						if (this.viewStyle == View.Tile)
						{
							this.UpdateTileView();
						}
					}
					else
					{
						base.UpdateStyles();
					}
					this.UpdateListViewItemsLocations();
				}
			}
		}

		[DefaultValue(false)]
		public bool VirtualMode
		{
			get { return HasStyle(NativeMethods.ListViewStyle.OwnerData); }
			set { SetStyle(NativeMethods.ListViewStyle.OwnerData, value); }
		}

		protected override CreateParams CreateParams
		{
			[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
			get
			{
				CreateParams createParams = base.CreateParams;
				createParams.ClassName = "SysListView32";
				if (base.IsHandleCreated)
				{
					int windowLong = this.GetStyle();
					createParams.Style |= windowLong & 0x300000; // WS_HSCROLL | WS_VSCROLL
				}
				createParams.Style |= (int)NativeMethods.ListViewStyle.ShareImageLists;
				if (this.alignStyle == ListViewAlignment.Left)
					createParams.Style |= 0x800;
				if (this.AutoArrange)
					createParams.Style |= 0x100;
				switch (this.borderStyle)
				{
					case BorderStyle.FixedSingle:
						createParams.Style |= 0x800000; // WS_BORDER
						break;

					case BorderStyle.Fixed3D:
						createParams.ExStyle |= 0x200; // LVS_EX_REGIONAL
						break;
				}
				switch (this.headerStyle)
				{
					case ColumnHeaderStyle.None:
						createParams.Style |= 0x4000; // LVS_NOCOLUMNHEADER
						break;

					case ColumnHeaderStyle.Nonclickable:
						createParams.Style |= 0x8000; // LVS_NOSORTHEADER
						break;
				}
				if (this.LabelEdit)
				{
					createParams.Style |= 0x200; // LVS_EDITLABELS
				}
				if (!this.LabelWrap)
				{
					createParams.Style |= 0x80; // LVS_NOLABELWRAP
				}
				if (!this.HideSelection)
				{
					createParams.Style |= 8; // LVS_SHOWSELALWAYS
				}
				if (!this.MultiSelect)
				{
					createParams.Style |= 4; // LVS_SINGLESEL
				}
				if (this.listItemSorter == null)
				{
					switch (this.sorting)
					{
						case SortOrder.Ascending:
							createParams.Style |= 0x10;
							break;

						case SortOrder.Descending:
							createParams.Style |= 0x20;
							break;
					}
				}
				if (this.VirtualMode)
				{
					createParams.Style |= 0x1000; // LVS_OWNERDATA
				}
				if (this.viewStyle != View.Tile)
				{
					createParams.Style |= (int)this.viewStyle;
				}
				if ((this.RightToLeft == RightToLeft.Yes) && this.RightToLeftLayout)
				{
					createParams.ExStyle |= 0x400000; // WS_EX_LAYOUTRTL
					createParams.ExStyle &= ~0x7000; // !(WS_EX_RIGHT | WS_EX_RTLREADING | WS_EX_LEFTSCROLLBAR)
				}
				return createParams;
			}
		}

		protected override Size DefaultSize
		{
			get { return defaultSize; }
		}

		private bool ComctlSupportsVisualStyles
		{
			get
			{
				if (!comctlSupportsVisualStylesTested)
				{
					comctlSupportsVisualStylesTested = true;
					comctlSupportsVisualStyles = Application.RenderWithVisualStyles;
				}
				return comctlSupportsVisualStyles;
			}
		}

		private bool FlipViewToLargeIconAndSmallIcon { get; set; }

		public void BeginUpdate()
		{
			if (this.IsHandleCreated)
			{
				if (this.updateCount == 0)
				{
					this.SendMessage(11, 0, 0);
				}
				this.updateCount = (short)(this.updateCount + 1);
			}
			if ((this.updateCounter++ == 0) && (tempItems == null))
			{
				tempItems = new List<ListViewItem>();
			}
		}

		public void EndUpdate()
		{
			if ((--this.updateCounter == 0) && (tempItems != null))
			{
				this.ApplyUpdateCachedItems();
			}
			if (this.updateCount > 0)
			{
				this.updateCount = (short)(this.updateCount - 1);
				if (this.updateCount == 0)
				{
					this.SendMessage(11, -1, 0);
					this.Invalidate();
				}
			}
		}

		internal void RecreateHandleInternal()
		{
			if (base.IsHandleCreated) // && (this.StateImageList != null))
				this.SendMessage(0x1003, 2, 0);
			base.RecreateHandle();
		}

		internal IntPtr SendMessage(uint msg, int wParam = 0, int lParam = 0)
		{
			if (this.IsHandleCreated)
				return NativeMethods.SendMessage(this.Handle, msg, wParam, lParam);
			return IntPtr.Zero;
		}

		protected override void CreateHandle()
		{
			if (!base.RecreatingHandle)
			{
				using (new NativeMethods.ThemingScope())
					NativeMethods.InitCommonControlsEx(NativeMethods.CommonControlClass.ListView);
			}
			base.CreateHandle();
		}

		protected override void Dispose(bool disposing)
		{
			if (disposing)
			{
			}
			base.Dispose(disposing);
		}

		protected override bool IsInputKey(Keys keyData)
		{
			if ((keyData & Keys.Alt) != Keys.Alt)
			{
				switch ((keyData & Keys.KeyCode))
				{
					case Keys.Prior:
					case Keys.Next:
					case Keys.End:
					case Keys.Home:
						return true;
				}
				if (base.IsInputKey(keyData))
				{
					return true;
				}
				/*				if (this.listViewState[0x4000])
								{
									switch ((keyData & Keys.KeyCode))
									{
										case Keys.Return:
										case Keys.Escape:
											return true;
									}
								}*/
			}
			return false;
		}

		protected override void OnBackgroundImageChanged(EventArgs e)
		{
			if (base.IsHandleCreated)
			{
			}
			base.OnBackgroundImageChanged(e);
		}

		private void ApplyUpdateCachedItems()
		{
			if (tempItems != null)
			{
				List<ListViewItem> items = new List<ListViewItem>(tempItems);
				tempItems = null;
				if (items.Count > 0)
					this.InsertItems(this.itemCount, items, false);
			}
		}

		private bool HasStyle(NativeMethods.ListViewStyle style)
		{
			return listViewStyle.IsFlagSet(style);
		}

		private void SetStyle(NativeMethods.ListViewStyle style, bool value)
		{
			if (HasStyle(style) != value)
			{
				EnumUtil.SetFlags(ref listViewStyle, style, value);
				UpdateStyles();
			}
		}

		private void UpdateGroupView()
		{
			if ((base.IsHandleCreated && this.ComctlSupportsVisualStyles) && !this.VirtualMode)
				this.SendMessage((uint)NativeMethods.ListViewMessage.EnableGroupView, this.GroupsEnabled ? 1 : 0, 0);
		}

		private void UpdateListViewItemsLocations()
		{
			if (((!this.VirtualMode && base.IsHandleCreated) && this.AutoArrange) && ((this.View == View.LargeIcon) || (this.View == View.SmallIcon)))
			{
				try
				{
					this.BeginUpdate();
					this.SendMessage((uint)NativeMethods.ListViewMessage.Update, -1, 0);
				}
				finally
				{
					this.EndUpdate();
				}
			}
		}
		private void UpdateStyles()
		{
			// TODO: Reset window styles
			this.OnStyleChanged(EventArgs.Empty);
		}

		private void UpdateTileView()
		{
			NativeMethods.LVTILEVIEWINFO lvtileviewinfo;
			lvtileviewinfo = new NativeMethods.LVTILEVIEWINFO(0)
			{
				MaxTextLines = (this.columnHeaders != null) ? this.columnHeaders.Length : 0,
				TileWidth = this.TileSize.Width,
				TileHeight = this.TileSize.Height
			};
			NativeMethods.SendMessage(base.Handle, NativeMethods.ListViewMessage.Update, 0, lvtileviewinfo);
		}

		internal class IconComparer : IComparer<ListViewItem>
		{
			public IconComparer(SortOrder currentSortOrder)
			{
				this.SortOrder = currentSortOrder;
			}

			public SortOrder SortOrder { private get; set; }

			public int Compare(ListViewItem item, ListViewItem item2)
			{
				if (this.SortOrder == SortOrder.Ascending)
					return string.Compare(item.Text, item2.Text, false, CultureInfo.CurrentCulture);
				return string.Compare(item2.Text, item.Text, false, CultureInfo.CurrentCulture);
			}
		}
	}
}
